/**
 * 
 */
package denaris.core;

import java.io.DataInputStream;
import java.io.InputStream;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.game.TiledLayer;

import denaris.core.controller.AbstractController;
import denaris.core.entity.AbstractEntity;
import denaris.core.entity.Enemy;
import denaris.core.entity.LooseSatellite;
import denaris.core.entity.Player;
import denaris.factory.FrameFactory;
import denaris.factory.ImageFactory;
import denaris.midlet.Denaris;
import denaris.midlet.GameScreen;
import denaris.midlet.DenarisGameCanvas.DisplayProperties;

/**
 * @author stkl23
 * 
 */
public class TiledMap extends AbstractDenarisMap {
	private Image tiles = null;

	private TiledLayer layer;

	private final int[] enemySequence = FrameFactory.createLoopbackSequence(0,
			4, 3);

	private int frame = 0;

	public TiledMap(String mapId, int width, int height, int cellWidth,
			int cellHeight) {
		int i = 0; 
		int j = 0;
		try {
			// load the tile image
			tiles = ImageFactory.getImage("/denaris/resource/map/" + mapId
					+ ".png");
			// load the tiles
			byte[] cells = new byte[width * height];
			InputStream resourceAsStream = this.getClass().getResourceAsStream("/denaris/resource/map/" + mapId + ".dat");
			DataInputStream dis = new DataInputStream(resourceAsStream);
			dis.readFully(cells);
			
			resourceAsStream.close();
			// create the Layer
			layer = new TiledLayer(width, height, tiles, cellWidth, cellHeight);
			// fill in the cell values
			for (i = 0; i < height; i++) {
				for (j = 0; j < width; j++) {
//					System.out.println("(" + i + "," + j + "): " + (cells[i * width + j] & 0x00FF));
					layer.setCell(j, i, (cells[i * width + j] & 0x00FF));
				}
			}
		} catch (Exception e) {
			Denaris.getInstance().handleException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see denaris.core.AbstractDenarisMap#initialize()
	 */
	public void initialize() {
		layer.setPosition(0, DisplayProperties.GAME_TOP);
		
		final Player player = (Player) getPlayerGroup();
		// add a collision listener to the player group
		player.setCollisionListener(new CollisionAdapter() {
			public void onCollision(AbstractEntity source,
					AbstractEntity opponent) {
				// we collided with some other ship
				player.explode(20, 3);
			}

			public void onCollision(AbstractEntity source,
					AbstractDenarisMap map) {
				// we collided with the map
				player.explode(20, 3);
			}
		});
		
		// reset the controller
		player.getController().reset();

		// add a Starfield to the map
		setStarfield(new Starfield(50, new int[] { 0xB6FAFA, 0xD27DED,
				0x4F44D8, 0x675282, 0x4B2C1F }));
	}

	public void paint(Graphics g) {
		int w = DisplayProperties.DISPLAY_WIDTH;
		int h = DisplayProperties.GAME_HEIGHT;

		g.setClip(0, DisplayProperties.GAME_TOP, w, h);
		g.setColor(0x000000);
		g.fillRect(0, DisplayProperties.GAME_TOP, w, h);

		// paint the starfield
		getStarfield().paint(g);

		// paint the layer
		layer.paint(g);

		// paint the sprites
		super.paint(g);
	}

	private Sprite getSprite() {
		Sprite s;
		s = new Sprite(ImageFactory
				.getImage("/denaris/resource/sprite/enemy_1.png"), 18, 16);
		s.setFrameSequence(enemySequence);
		return s;
	}

	public void update() {
		super.update();
		// scroll the layer
		layer.move(-GameScreen.SCROLL_SPEED, 0);
		getStarfield().update();
		frame++;
		if (frame % 15 == 0) {
			// add a new enemy
			Enemy enemy = new Enemy(this, 500);
			AbstractController controller = enemy.getController();
			Sprite s = getSprite();
			controller.setSprite(s);
			int y = GameScreen.nextPositiveInt(DisplayProperties.GAME_HEIGHT) + DisplayProperties.GAME_TOP;
			controller.getSprite().setPosition(DisplayProperties.DISPLAY_WIDTH, y);
			addEnemy(enemy);
		} else if (frame % 123 == 0) {
			// add a new satellite
			LooseSatellite satellite = new LooseSatellite(this, GameScreen.nextPositiveInt(5));
			satellite.getController().getSprite().setPosition(DisplayProperties.DISPLAY_WIDTH,	GameScreen.nextPositiveInt(DisplayProperties.GAME_HEIGHT) + DisplayProperties.GAME_TOP);
			// the loose satellite is an enemy - it can destroy the player
			addEnemy(satellite);
		}
		// collision detection
		getPlayerGroup().checkCollision();
	}

	public boolean collidesWith(Sprite sprite) {
		return sprite.collidesWith(layer, true);
	}

	public int getHeight() {
		return layer.getHeight();
	}

	public int getWidth() {
		return layer.getWidth();
	}

	public int getX() {
		return layer.getX();
	}

	public int getY() {
		return layer.getY();
	}

	public void move(int dx, int dy) {
		layer.move(dx, dy);
		getStarfield().moveVertical(dy);
		// move all enemies and friends
		scrollVertical(getEnemies(), dy);
		scrollVertical(getFriends(), dy);
		// move collision particles
		getCollisionParticleSystem().scrollVertical(dy);
	}

	public TiledLayer getLayer() {
		return layer;
	}
	
	public int cellAtColRow(int col, int row) {
		if (col > 0 && row > 0 && col < layer.getColumns() && row < layer.getRows()) {
			return layer.getCell(col, row);
		} else {
			return 0;
		}
		
	}

	public int cellAt(int x, int y) {
		return cellAtColRow(getCol(x), getRow(y));
	}
	
	public int getCol(int x) {
		return (-layer.getX() + x) / layer.getCellWidth();
	}
	
	public int getRow(int y) {
		return (-layer.getY() + y) / layer.getCellHeight();
	}

	public void reset() {
		layer.setPosition(0, layer.getY());
		Player p = GameScreen.getInstance().getPlayer();
		// set the player object
		setPlayerGroup(p);
		p.reset();
		// reset the starfield
		getStarfield().reset();

		// remove all friends and enemies
		getFriends().removeAllElements();
		getEnemies().removeAllElements();
	}
}
